/**
 * @brief MCU 中断管理器适配接口实现
 * @author 张勇 41204@qq.com
 * @date 2022-04
 */
 
#include <stdint.h>
#include <stm32f10x.h>
#include <cmsis_armcc.h> // for __set_FAULTMASK()

#include "share/cccompat.h"
#include "share/utils.h"
#include "mcu.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "MCU"
#include "share/log4app.h"

#if defined __ICCARM__
	// IAR Compiler
	#include <intrinsics.h>
	// 中断状态数据类型
	//typedef __istate_t mcu_istate_t;
	void mcu_disable_interrupts()           		{ __disable_interrupt(); }
	void mcu_enable_interrupts()            		{ __enable_interrupt(); }
	mcu_istate_t mcu_get_interrupts_state()			{ return (mcu_istate_t) __get_interrupt_state(); }
	void mcu_set_interrupts_state(mcu_istate_t x)	{ __set_interrupt_state(x); }
#elif defined __ARMCC_VERSION
	/// 中断状态数据类型
	typedef uint32_t mcu_istate_t;
	void mcu_disable_interrupts()					{ __set_PRIMASK(1); } // __ASM volatile("cpsid i")
	void mcu_enable_interrupts()					{ __set_PRIMASK(0); } // __ASM volatile("cpsie i")
	mcu_istate_t mcu_get_interrupts_state()			{ return (mcu_istate_t) __get_PRIMASK(); }
	void mcu_set_interrupts_state(mcu_istate_t x)	{ __set_PRIMASK(x); }
#else
	#error "No IRQ enable/disable functions !"
#endif

// 中断向量映射表项数据结构
typedef struct {
	int irqn;
	mcu_isr_t handler;
	void *cookie;
} irq_handler_map_t;

// 中断向量映射表
#define MAX_IRQ_MAP 32
static irq_handler_map_t _irq_maps[MAX_IRQ_MAP] = {{0, NULL}};

std_err_t mcu_intc_register_handler(mcu_intc_t intc, uint32_t irqn, mcu_isr_t isr, void *cookie) {
	if(!isr) { return STD_ERR_INVALID_ARG; }
	
	// 避免重复记录，并找到第一个空位置
	int firstEmptyIdx = -1;
	for(int i=0; i < MAX_IRQ_MAP; i++) {
		// 如果已经注册了，则直接返回
		if(_irq_maps[i].irqn == irqn && _irq_maps[i].handler == isr) { return STD_ERR_OK; }
		// 记录第一个空位置
		if(firstEmptyIdx < 0 && !_irq_maps[i].handler) { firstEmptyIdx = i; }
	}
	if(firstEmptyIdx < 0) { return STD_ERR_NO_RESOURCES; }
	
	// 保存记录
	_irq_maps[firstEmptyIdx].irqn = irqn;
	_irq_maps[firstEmptyIdx].handler = isr;
	_irq_maps[firstEmptyIdx].cookie = cookie;
	return STD_ERR_OK;
}

void mcu_intc_unregister_handler(uint32_t irqn, mcu_isr_t isr) {
	if(!isr) { return; }
	for(int i=0; i < MAX_IRQ_MAP; i++) {
		if(_irq_maps[i].irqn == irqn && _irq_maps[i].handler == isr) { _irq_maps[i].handler = NULL; return; }
	}
}

_RAMCODE_ void mcu_call_irq_handlers(int irqn) {
	for(int i=0; i < MAX_IRQ_MAP; i++) {
		if(_irq_maps[i].irqn == irqn) { _irq_maps[i].handler(_irq_maps[i].cookie); }
	}
}

bool mcu_validate_intvect_entry(uint32_t entry) {
	uint32_t msp   		= *((uint32_t*)entry);
	uint32_t entryFunc 	= *((uint32_t*)entry+4);
	return mcu_is_in_ram(msp, 4) && mcu_is_in_flash(entryFunc, 4);
}

mcu_intc_t mcu_intc() {
	// STM32 只有一个中断控制器，不用实例化
	return NULL;
}

/**
 * @brief 初始化中断管理策略
 * 抢占优先级​​：高抢占优先级可打断低抢占优先级的中断（嵌套）。
​​ * 子优先级​​：仅用于决定多个挂起中断的执行顺序，​​不能打断正在执行的中断​​。
​​ * 同级中断​​：若两个中断抢占优先级相同，即使子优先级不同，也无法互相嵌套。
 * 优先级数值越小，优先级越高（0为最高）。
 * 若抢占优先级相同，子优先级高的先执行；若均相同，按硬件中断号排序。
 */
#define NVIC_PRIORITYGROUP_0         0x00000007U /*!< 0 bits for pre-emption priority, 4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1         0x00000006U /*!< 1 bits for pre-emption priority, 3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2         0x00000005U /*!< 2 bits for pre-emption priority, 2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3         0x00000004U /*!< 3 bits for pre-emption priority, 1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4         0x00000003U /*!< 4 bits for pre-emption priority, 0 bits for subpriority */

std_err_t mcu_intc_init() {
	// 设置优先级分组: NVIC_PRIORITYGROUP_4​​：仅抢占优先级（16级），无子优先级，适合简单应用。
	NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
	// 系统异常​​（如 SysTick、PendSV）：优先级通过 SHPRx 寄存器配置，优先级字段为 ​​8位​​（实际高4位有效）
	NVIC_SetPriority(SysTick_IRQn, (1UL << 4) - 1UL); // 设置 SysTick 为最低优先级
	// 硬件优先级​​：某些内核中断（如 Reset、NMI）优先级固定为负值，不可配置。
	return STD_ERR_OK;
}

std_err_t mcu_intc_set_priority(mcu_intc_t intc, uint32_t irqn, uint8_t priority) {
    NVIC_SetPriority((IRQn_Type)irqn, priority);
	return STD_ERR_OK;
}

std_err_t mcu_intc_enable(mcu_intc_t intc, uint32_t irqn, bool enable) {
	if(enable) {
		NVIC_EnableIRQ((IRQn_Type)irqn);
	} else {
		NVIC_DisableIRQ((IRQn_Type)irqn);
	}
	return STD_ERR_OK;
}

// 跳转程序类型
typedef void (*FUNC_startupJumper)();

void mcu_jump_to_intvect(uint32_t entry) {
	if( !mcu_validate_intvect_entry(entry) ) {
		loge("Can't jump to invalid entry: 0x%08lX\r\n", entry);
		return;
	}
	logd("Jump to Entry at 0x%08lX ...\r\n", entry);

	// 初始化程序入口地址
	uint32_t msp = *((uint32_t*)entry);               // 栈顶位置 (见 icf/map 中的 CSTACK 地址)
	uint32_t entryFunc = *((uint32_t*)(entry+4));     // __iar_program_start 地址？
	FUNC_startupJumper startupJumper = (FUNC_startupJumper)((void*)entryFunc);

	// 必须先关闭全局中断
	mcu_disable_interrupts();
	// 设置栈地址
	__set_MSP(msp);
	// 跳转到程序
	startupJumper();
}

/*
解决stm32 ota程序跳转异常：
  ota升级分启动程序BootLoader与跳转的应用程序app,但跳转APP程序后有些中断不关闭，会导致app程序异常重启，所以防止app跳转程序异常需要关闭所有的中断。
解决办法：
1. 在BootLoader跳转前关闭所有的中断。
2. 在BootLoader跳转后app程序刚启动关闭所有的中断。
我更倾向与方法2，可以解决与BootLoader程序的耦合性。不用管BootLoader是否关闭中断了.

过去，对中断的使能和除能是通过修改 CPSR 的，在 CM3 中没有 CPSR，而是使用 PRIMASK 或 FAULTMASK 来实现全局中断的开关。

// 关闭全局中断
DISABLE_INT();

// 关闭滴答定时器，复位到默认值
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;

// 设置所有时钟到默认状态，使用HSI时钟
HAL_RCC_DeInit();

// 关闭所有中断，清除所有中断挂起标志
for (int i = 0; i < 8; i++) {
	NVIC->ICER[i]=0xFFFFFFFF;
	NVIC->ICPR[i]=0xFFFFFFFF;
}

// 使能全局中断
ENABLE_INT();
*/
